import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

/**
 * This class provides functionality for the application to be ran
 * successfully. Reading in files instantiating the objects and 
 * orchastrating the execution of Prim's algorithm.
 * 
 * @author Kyle Scharnhorst
 * @author Erica Putsche
 *
 */
public class PowerGridApp {
	
	/**
	 * Infinity constant.
	 */
	private static double INFINITY = Double.POSITIVE_INFINITY;
	
	/**
	 * The heap.
	 */
	private static BinaryHeap my_heap;
	
	/**
	 * The frame for the GUI.
	 */
	private static JFrame my_frame = new JFrame();

	/**
	 * The text field that will take in the test file path.
	 */
	private static JTextField my_path_field = new JTextField();
	
	/**
	 * The area in which the results of prim's algorithm will be drawn.
	 */
	private static JTextArea my_draw_area = new JTextArea();
	
	/**
	 * The simple graph.
	 */
	private static SimpleGraph my_simple_graph = new SimpleGraph(); 
	
	/**
	 * Main method for this application.
	 * 
	 * @param args input arguments. There won't be any.
	 */
	public static void main(String[] args) {
		buildGui();		
	}
	
	/**
	 * This method builds the gui.
	 */
	private static void buildGui() {
		//initializing components
		JPanel main_panel = new JPanel(new BorderLayout());
		JPanel input_panel = new JPanel(new FlowLayout());
		JPanel drawing_panel = new JPanel();
		final JButton submit_button = new JButton("Submit");
		
		//setup components
		my_path_field.setPreferredSize(new Dimension(300, 27));
		
		input_panel.add(submit_button);
		input_panel.add(my_path_field);
		
		drawing_panel.add(my_draw_area);
		
		main_panel.add(input_panel, BorderLayout.SOUTH);
		main_panel.add(drawing_panel, BorderLayout.CENTER);
		
		my_frame.add(main_panel);
		
		submit_button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent a) {
				//builds the simple graph
				GraphInput.LoadSimpleGraph(my_simple_graph, my_path_field.getText());
				//D:\UWTacoma\CSS343-algorithms\HW6B\algorithmsPrimPowerGrid\src\test.txt
				
				//print out edges
				List<Edge> minimum_spanning_tree_edges = prim(my_simple_graph);
				String s = "";
				for (Edge e : minimum_spanning_tree_edges) {
					String temp = e.getFirstEndpoint().getName() + " " + e.getSecondEndpoint().getName() + " " + e.getData().toString() + "\n";
					s += temp;
					//System.out.println(temp); //prints to console
				}
				my_draw_area.setText(s); //to gui
				my_frame.pack(); //resize frame to fit the edges displayed.
				my_simple_graph = new SimpleGraph(); 
			}
			
		});
		
		my_frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		my_frame.pack();
		my_frame.setVisible(true); //display
	}
	
	/**
	 * This method takes in a simple graph and applies Prim's algorithm to solve the 
	 * minimum spanning tree problem on that graph.
	 * @param some_simple_graph The graph to solve the minimum spanning tree problem on.
	 * @return A list of Edges representing the minimum spanning tree.
	 */
	private static List<Edge> prim(SimpleGraph some_simple_graph) {
		
		my_heap = new BinaryHeap(some_simple_graph.numVertices());
		List<Vertex> known_vertices = new ArrayList<Vertex>();
		List<Edge> min_edges = new ArrayList<Edge>();

		
		Iterator<Vertex> vertices;
		Iterator<Vertex> i;
		//initialize set of known vertices.
		vertices = some_simple_graph.vertices();
		Vertex v;
		v = (Vertex)vertices.next();
		known_vertices.add(v);
		PrimHeapNode node = new PrimHeapNode(Double.NEGATIVE_INFINITY, v, 1);
		v.setData(node);
		
		//create prim nodes and put them in heap.
		int counter = 1;
		for (i = vertices; i.hasNext(); counter++) {
			
            v = (Vertex) i.next();
            
            node = new PrimHeapNode(INFINITY, v, counter);
            v.setData(node);
            my_heap.insert(node);

		}
		
		Iterator<Edge> edges; 
		Edge edge;  
		PrimHeapNode node_to_update; 
		
		//for all vertices
		for (int j = 0; j < some_simple_graph.numVertices()-1; j++) {
			
			//update edges and take the vertice with the least label and add it to known and add
			//that edge to the list of edges to be returned.
			for (edges = some_simple_graph.incidentEdges(known_vertices.get(j)); edges.hasNext(); ) {
				edge = (Edge) edges.next(); 
				
				node_to_update = (PrimHeapNode) edge.getSecondEndpoint().getData();

				//find prim node for endpoint
				if (node_to_update.getLabel() > (Double)edge.getData()) {
					node_to_update.setLabel((Double)edge.getData(), edge);
				}
			}
			
			//percolate
			for (int n = 1; n <= my_heap.size; n++) {
				my_heap.percolateUp(my_heap.getElements()[n].getHeap_position());
			}
			
			//get min vert
			PrimHeapNode minimum = null;
			try {
				minimum = (PrimHeapNode) my_heap.popMin();
			} catch (EmptyHeapException e) {
				e.printStackTrace();
			}
			
			//get min edge and store in list of edges.
			known_vertices.add(minimum.getMy_vertex());
			min_edges.add(minimum.getMy_min_edge());
		}	
		return min_edges;
	}

}
